package com.css.sword.knowing.search;

import com.alibaba.druid.util.StringUtils;
import com.css.sword.knowing.search.aggregation.*;
import com.css.sword.knowing.search.comm.SearchConst;
import com.css.sword.knowing.search.comm.SearchPriv;
import com.css.sword.knowing.search.exception.ArgException;
import com.css.sword.knowing.search.util.SearchUtil;
import com.css.sword.knowing.search.util.ToJson;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.MultiSearchRequestBuilder;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequestBuilder;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.MetricsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.avg.Avg;
import org.elasticsearch.search.aggregations.metrics.max.Max;
import org.elasticsearch.search.aggregations.metrics.min.Min;
import org.elasticsearch.search.aggregations.metrics.stats.Stats;
import org.elasticsearch.search.aggregations.metrics.stats.extended.ExtendedStats;
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCount;
import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.internal.InternalSearchHitField;
import org.elasticsearch.search.sort.SortOrder;

import java.io.IOException;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 索引操作入口类，包括索引的创建、检索、删除等操作入口
 */
public class Searcher {

    /*
     * 调试标记，打开此标记会保留查询语句等信息，并随结果返回
     */
    public static boolean Debug = false;
    public static String file_url = "https://csfile.szoa.sz.gov.cn/file/download?identityId=szoa_es&token=SZOA_ES!1008&md5Path=";

    private static TransportClient client = null;

    /**
     * 获取搜索服务器的链接
     * 
     */
    static {
        try {
            int port = 0;
            String address = null;

            Properties p = SearchUtil.getProperties();
            
            List aps = SearchUtil.getAddress(p);
            String url = p.getProperty("file_url");
            if(!StringUtils.isEmpty(url))
                file_url = url;
            String clusterName = p.getProperty("clusterName");
            
            Settings settings = Settings.settingsBuilder()
            		.put("cluster.name", clusterName)
                    .put("client.transport.sniff", true).build();
            
            client = TransportClient.builder().settings(settings).build();

            for (int i = 0; i < aps.size(); i++) {

                address = ((String[]) aps.get(i))[0];

                if (((String[]) aps.get(i)).length == 1) {
                    port = 9300;
                } else {
                    port = Integer.parseInt(((String[]) aps.get(i))[1]);
                }
                client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(address), port));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取服务器链接
     * 
     * @return 返回服务器连接句柄
     */
    public static synchronized TransportClient getTransportClient() {
        return client;
    }
    
    /**
     * 获取搜索服务器的链接
     * 
     */
    private static void getClient() throws IOException {

        Lock lock = new ReentrantLock();

        try {

            lock.lock();

            if (client == null) {
                int port = 0;
                String address = null;

                Properties p = SearchUtil.getProperties();

                List aps = SearchUtil.getAddress(p);

                String clusterName = p.getProperty("clusterName");

                Settings settings = Settings.settingsBuilder()
                		.put("cluster.name", clusterName)
                        .put("client.transport.sniff", true).build();
                
                TransportClient tclient = TransportClient.builder().settings(settings).build();

                for (int i = 0; i < aps.size(); i++) {

                    address = ((String[]) aps.get(i))[0];

                    if (((String[]) aps.get(i)).length == 1) {
                        port = 9300;
                    } else {
                        port = Integer.parseInt(((String[]) aps.get(i))[1]);
                    }
                    tclient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(address), port));
                }
                client = tclient;
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 类被破坏时，释放链接
     * 
     */
    @Override
    protected void finalize() throws Throwable {
        release();
    }

    /**
     * 释放已获取的服务器链接句柄，同时连接池也一并释放。单次测试时可以调用此接口； 正式对外的检索服务不要调用。
     */
    public static void release() {
        if (client != null)
            client.close();
        client = null;
    }

    /**
     * 创建索引
     * 
     * @param ii
     *            创建索引输入 ，详见IndexInput类
     * @return 如果完全创建成功，返回null，否则返回错误消息
     * @throws IOException
     * @throws ArgException
     */
    public static String createIndex(IndexInput ii) throws IOException, ArgException {

        String errMsg = "";

        getClient();
        if (client != null) {
            IndexRequestBuilder irb = client.prepareIndex(ii.getIndexName(), ii.getTypeName());// .setRefresh(true);
            BulkRequestBuilder bulkRequest = client.prepareBulk();

            for (int i = 0; i < ii.getIndexDocs().size(); i++) {
                XContentBuilder builder = ToJson.getJsonBuilder(ii.getIndexDocs().get(i).getData(), ii.getSystemID());

                irb = client.prepareIndex(ii.getIndexName(), ii.getTypeName());// .setRefresh(true);
                irb.setSource(builder);

                String key = ii.getIndexDocs().get(i).getKey();
                if (null != key && !key.isEmpty()) {
                    irb.setId(key);
                }

                String routing = ii.getIndexDocs().get(i).getRouting();
                if (null != routing && !routing.isEmpty()) {
                    irb.setRouting(routing);
                }

                bulkRequest.add(irb);
                // bulkRequest.add(client.prepareIndex(ii.getIndexName(),
                // ii.getType()).setRefresh(true).setSource(ii.getData()));
            }
            bulkRequest.setRefresh(true);
            BulkResponse bulkResponse = bulkRequest.execute().actionGet();
            if (bulkResponse.hasFailures()) {
               // errMsg = bulkResponse.buildFailureMessage();
                for(BulkItemResponse brps :bulkResponse.getItems())
                {
                	if(null != brps.getFailure())
                	{
                		errMsg += brps.getIndex();
                		errMsg += ", ";
                		errMsg += brps.getType();
                		errMsg += ", ";
                		errMsg += brps.getId();
                		errMsg += ", ";
                		errMsg += brps.getFailureMessage();
                		errMsg += "\n";
                	}
                }
            }

        }
        return errMsg;
    }
    
    public static String updateIndex(IndexInput ii) throws IOException, ArgException {

        String errMsg = "";

        getClient();
        if (client != null) {
            BulkRequestBuilder bulkRequest = client.prepareBulk();

            for (int i = 0; i < ii.getIndexDocs().size(); i++) {
                XContentBuilder builder = ToJson.getJsonBuilder(ii.getIndexDocs().get(i).getData(), ii.getSystemID());

                String id = ii.getIndexDocs().get(i).getKey();
                if(null==id || id.isEmpty())
                	continue;
                
                UpdateRequestBuilder urb = client.prepareUpdate(ii.getIndexName(), ii.getTypeName(), id);
                urb.setDoc(builder);
                
                bulkRequest.add(urb);
            }
            bulkRequest.setRefresh(true);
            BulkResponse bulkResponse = bulkRequest.execute().actionGet();
            if (bulkResponse.hasFailures()) {
               // errMsg = bulkResponse.buildFailureMessage();
                for(BulkItemResponse brps :bulkResponse.getItems())
                {
                	if(null != brps.getFailure())
                	{
                		errMsg += brps.getIndex();
                		errMsg += ", ";
                		errMsg += brps.getType();
                		errMsg += ", ";
                		errMsg += brps.getId();
                		errMsg += ", ";
                		errMsg += brps.getFailureMessage();
                		errMsg += "\n";
                	}
                }
            }

        }
        return errMsg;
    }
    
    public static String updateIndex(String indexName, String typeName, String id, String dataJson){

		String errMsg = "";

		try {
			getClient();
		
		if (client != null) {
			
			UpdateRequestBuilder request = client.prepareUpdate(indexName, typeName, id).setRefresh(true);

			UpdateResponse res = request.setDoc(dataJson).execute().actionGet();

			if (res.getShardInfo().getFailed() > 0) {
				errMsg += indexName;
				errMsg += ", ";
				errMsg += typeName;
				errMsg += ", ";
				errMsg += id;
				errMsg += ", ";
				errMsg += "update faild!";
				errMsg += "\n";
			}
		}
		} catch (Exception e) {
			// TODO Auto-generated catch block
            errMsg = "errMsg: ";
			errMsg += indexName;
			errMsg += ", ";
			errMsg += typeName;
			errMsg += ", ";
			errMsg += id;
			errMsg += ", ";
			errMsg += e.getMessage();
		}
		return errMsg;
	}

	public static String upsertIndex(String indexName, String typeName, String id, String dataJson){

		String errMsg = "";

		try {
			getClient();
		
		if (client != null) {
			
			UpdateRequestBuilder request = client.prepareUpdate(indexName, typeName, id).setRefresh(true);

			UpdateResponse res = request.setDoc(dataJson).setUpsert(dataJson).execute().actionGet();

			if (res.getShardInfo().getFailed() > 0) {
				errMsg += indexName;
				errMsg += ", ";
				errMsg += typeName;
				errMsg += ", ";
				errMsg += id;
				errMsg += ", ";
				errMsg += "upsert faild!";
				errMsg += "\n";
			}
		}
		} catch (Exception e) {
			// TODO Auto-generated catch block
            errMsg = "errMsg: ";
			errMsg += indexName;
			errMsg += ", ";
			errMsg += typeName;
			errMsg += ", ";
			errMsg += id;
			errMsg += ", ";
			errMsg += e.getMessage();
		}
		return errMsg;
	}

	/**
     * 创建索引
     * 
     * @param ii
     *            创建索引输入 ，详见IndexInput类
     * @return 如果完全创建成功，返回null，否则返回错误消息
     * @throws IOException
     * @throws ArgException
     */
    public static String upsertIndex(IndexInput ii) throws IOException, ArgException {

        String errMsg = "";

        getClient();
        if (client != null) {
            
            BulkRequestBuilder bulkRequest = client.prepareBulk();

            for (int i = 0; i < ii.getIndexDocs().size(); i++) {
                XContentBuilder builder = ToJson.getJsonBuilder(ii.getIndexDocs().get(i).getData(), ii.getSystemID());

                String id = ii.getIndexDocs().get(i).getKey();
                if (null == id || id.isEmpty()) {
                    continue;
                }
                UpdateRequestBuilder urb = client.prepareUpdate(ii.getIndexName(), ii.getTypeName(), id);
                
                urb.setDoc(builder).setUpsert(builder);

                bulkRequest.add(urb);
            }
            bulkRequest.setRefresh(true);
            BulkResponse bulkResponse = bulkRequest.execute().actionGet();
            if (bulkResponse.hasFailures()) {
               // errMsg = bulkResponse.buildFailureMessage();
                for(BulkItemResponse brps :bulkResponse.getItems())
                {
                	if(null != brps.getFailure())
                	{
                		errMsg += brps.getIndex();
                		errMsg += ", ";
                		errMsg += brps.getType();
                		errMsg += ", ";
                		errMsg += brps.getId();
                		errMsg += ", ";
                		errMsg += brps.getFailureMessage();
                		errMsg += "\n";
                	}
                }
            }

        }
        return errMsg;
    }
    
	public static String createIndex(String indexName, String typeName, String id, String dataJson){

		String errMsg = "";

		try {
			getClient();

			if (client != null) {

				IndexRequestBuilder request = client.prepareIndex(indexName, typeName, id).setRefresh(true);
				//IndexResponse res = request.setSource(dataJson).setOpType(IndexRequest.OpType.CREATE).execute().actionGet();
                IndexResponse res = request.setSource(dataJson).execute().actionGet();

				if (res.getShardInfo().getFailed() > 0) {
					errMsg += indexName;
					errMsg += ", ";
					errMsg += typeName;
					errMsg += ", ";
					errMsg += id;
					errMsg += ", ";
					errMsg += "create faild!";
					errMsg += "\n";
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
            errMsg = "errMsg: ";
			errMsg += indexName;
			errMsg += ", ";
			errMsg += typeName;
			errMsg += ", ";
			errMsg += id;
			errMsg += ", ";
			errMsg += e.getMessage();
		}
		return errMsg;
	}

    /**
     * 创建带搜索提示索引，即自动补全检索词前缀
     * 
     * @param suggestInput
     *            见 SuggestInput 类
     * @return 创建成功返回 null， 不成功返回错误信息
     * @throws IOException
     * @throws ArgException
     */
    public static String createIndexForSuggest(SuggestInput suggestInput) throws IOException, ArgException {

        String errMsg = "";

        getClient();
        if (client != null) {
            IndexRequestBuilder irb = client.prepareIndex(suggestInput.getIndexName(), suggestInput.getType())
                    .setRefresh(true);

            irb.setSource(ToJson.getSuggestJson(suggestInput));

            IndexResponse ir = irb.execute().actionGet();

            if (!ir.isCreated()) {
                errMsg = "没有成功创建索引";
            }

        }
        return errMsg;
    }

    private static Map setDfaultField(Map map, SearchHit hit, Map<String, Boolean> returnFields)
    {
    	if(returnFields.containsKey("_id"))
    		map.put("_id", hit.getId());
        if(returnFields.containsKey("_score"))
        	map.put("_score", hit.getScore());
        if(returnFields.containsKey("_type"))
        	map.put("_type", hit.getType());
        if(returnFields.containsKey("_nodeId"))
        	map.put("_nodeId", hit.getShard().getNodeId());
        if(returnFields.containsKey("_shardId"))
        	map.put("_shardId", hit.getShard().getShardId());
        
    	return map;
    }
    
    private static void getData(List<Map> list, SearchHit hit, Map<String, Boolean> returnFields) {

        Map<String, HighlightField> result = hit.highlightFields();

        Map l = null;

        if (hit.getSource() != null) {
            // hit.getId();
            l = hit.getSource();
            
            l = setDfaultField(l, hit, returnFields);
            // list.add(s);
        } else {
            Map m = hit.getFields();
            Set keys = m.keySet();

            l = new HashMap();

            l = setDfaultField(l, hit, returnFields);
            
            for (Object key : keys) {
                Object value = m.get(key);
                if(null ==returnFields || returnFields.size() == 0)
                {
                    if (value instanceof InternalSearchHitField) {
                        String v = ((InternalSearchHitField) value).getValues().toString();
                        v = v.substring(1, v.length() - 1);
                        l.put(key, v);
                    }
                }
                else
                {                    
                    if(!returnFields.get(key) || ((InternalSearchHitField) value).isMetadataField())
                    {
                        l.put(key, ((InternalSearchHitField) value).getValue());
                    }
                    else
                    {
                        l.put(key, ((InternalSearchHitField) value).getValues());
                    }
                }
            }
        }

        for (String fieldName : result.keySet()) {
            HighlightField highLightField = result.get(fieldName);

            Text[] highLightTexts = highLightField.fragments();

            String highLightValue = "";
            for (Text text : highLightTexts) {
                highLightValue += text;
            }

            l.put(fieldName, highLightValue);
        }

        list.add(l);
    }

    /**
     * 同时对一个或者多个索引进行检索，可以是多个条件多个字段的检索
     * 
     * @param sis
     *            检索输入列表，每个检索对应一个SearchInput，详细见SearchInput类
     * @return 返回SearchResult结果列表，每个结果对应一个SearchResult, 详细见SearchResult类
     * @throws ArgException
     * @throws IOException
     */
    public static List<SearchResult> multiIndexSearch(List<SearchInput> sis) throws ArgException, IOException {

        List<SearchResult> srList = new ArrayList<SearchResult>();

        getClient();

        MultiSearchRequestBuilder mrb = client.prepareMultiSearch();
        for (SearchInput si : sis) {
            SearchUtil.validateSI(si);
            // BoolQueryBuilder queryBuilder =
            // getMutilValueQueryBuilder(si.getSearchFields()[0].getField(),si.getSearchFields()[0].getSearchValue(),si.getSystemID());

            BoolQueryBuilder queryBuilder = getMutilValueMutilFieldQueryBuilder(si.getSearchFields(),
                    si.getSearchOperator(), si.isUseSystemID()?si.getSystemID():"");
            
            SearchRequestBuilder srb = client.prepareSearch(si.getIndexName()).setQuery(queryBuilder);

            String[] typeNames = si.getTypeNames();
            if (typeNames != null && typeNames.length > 0)
                srb.setTypes(typeNames);

            String[] routings = si.getRoutings();
            if (routings != null && routings.length > 0)
                srb.setRouting(routings);

            highLight(si.getHighLightFields(), srb);

            sort(si.getOrderFields(), srb);

            String[] returnFields = si.getReturnFields();
            if (returnFields != null && returnFields.length != 0) {
                srb.addFields(returnFields);
            }

            srb.setFrom(si.getFrom()).setSize(si.getSize());
            srb.setPreference(si.getPreference());

            SearchResult sr = new SearchResult();
            sr.setPageNum(si.getFrom()/si.getSize());
            sr.setPageSize(si.getSize());
            if (Debug)
                sr.setQuery(srb.toString());

            mrb.add(srb);

            srList.add(sr);
        }
        MultiSearchResponse response = mrb.execute().actionGet();
        

        int index = 0;
        for (MultiSearchResponse.Item item : response.getResponses()) {
            SearchResponse searchResponse = item.getResponse();
            if (searchResponse != null) {
                SearchHits hits = searchResponse.getHits();

                SearchResult sr = srList.get(index);
                List<Map> list = new ArrayList<Map>();

                sr.setTotalNum(hits.getTotalHits());
                sr.setNum(hits.getHits().length);

                for (SearchHit hit : hits.getHits()) {
                    getData(list, hit, sis.get(index).getReturnFieldsAsHash());
                }
                sr.setData(list);
            }
            else
            {
            	if(item.getFailure() != null)
            	{
            		SearchResult sr = srList.get(index);
            		sr.setErrMsg(item.getFailure().getCause().toString());            		
            	}
            }
            index++;
        }
        return srList;
    }
    
    /**
     * 根据索引_id值确认文档是否存在
     * 
     * @param indexName
     *            索引名称
     * @param typeName
     *            类型名称
     * @param id
     *            id值
     * @return true:存在  false:不存在
     */
    public static boolean docExist(String indexName, String typeName, String id)
    {
    	if(null == indexName || indexName.isEmpty())
    		return false;

    	if(null == id || id.isEmpty())
    		return false;
    	
		try {
			getClient();

			GetRequestBuilder req = client.prepareGet().setIndex(indexName).setId(id).setFetchSource(false);
	    	if(null != typeName && !typeName.isEmpty())
	    		req.setType(typeName);
	    	GetResponse res = req.execute().actionGet();
			return res.isExists();
			
		} catch (Exception e) {
			e.getMessage();
		}

		return false;
	}
    
    /**
     * 根据索引_id值获取索引数据
     * 
     * @param indexName
     *            索引名称
     * @param typeName
     *            类型名称
     * @param id
     *            id值
     * @return 返回Map<String, Object>结果
     * @throws IOException
     */
    public static Map<String, Object> fetchDoc(String indexName, String typeName, String id) throws IOException
    {
    	if(null == indexName || indexName.isEmpty())
    		return null;

    	if(null == id || id.isEmpty())
    		return null;
    	
    	getClient();

    	GetRequestBuilder req = client.prepareGet().setIndex(indexName).setId(id);
    	if(null != typeName && !typeName.isEmpty())
    		req.setType(typeName);
    	GetResponse res = req.execute().actionGet();
    	
    	return res.getSource();
    }
    
    private static void highLight(HighLightField[] highLightFields, SearchRequestBuilder srb) {
        if (highLightFields != null) {
            for (HighLightField hlf : highLightFields) {
                if (hlf.getFieldName() != null) {
                    srb.addHighlightedField(hlf.getFieldName(), hlf.getFragmentSize());
                    srb.setHighlighterPreTags(hlf.getHighlighterPreTags());
                    srb.setHighlighterPostTags(hlf.getHighlighterPostTags());
                }
            }
        }
    }

    private static void sort(OrderField[] orderFields, SearchRequestBuilder srb) {
        if (orderFields != null) {
            for (OrderField of : orderFields) {
                if (of.getFieldName() != null) {
                    if (of.getOrder() == SearchConst.SEARCH_ORDER_TYPE_DESC) {
                        srb.addSort(of.getFieldName(), SortOrder.DESC);
                    } else {
                        srb.addSort(of.getFieldName(), SortOrder.ASC);
                    }
                }
            }
        }
    }

    private static BoolQueryBuilder getMutilValueMutilFieldQueryBuilder(SearchField[] fields, String searchOperator,
            String systemID) {
        String privs = SearchPriv.getPriv(systemID);
        String[] privArray = privs.split(",");

        BoolQueryBuilder rootQueryBuilder = QueryBuilders.boolQuery();
        if (null != fields) {
            QueryBuilder fieldQueryBuilder = getQuery(fields);

            if (SearchConst.SEARCH_OPERATOR_SHOULD.equalsIgnoreCase(searchOperator)) {
                rootQueryBuilder.should(fieldQueryBuilder);
            } else if (SearchConst.SEARCH_OPERATOR_MUST_NOT.equalsIgnoreCase(searchOperator)) {
                rootQueryBuilder.mustNot(fieldQueryBuilder);
            } else {
                rootQueryBuilder.must(fieldQueryBuilder);
            }

            if(null!=systemID && !systemID.isEmpty())
            	rootQueryBuilder.must(QueryBuilders.termsQuery(SearchConst.SYSTEMID_FIELD, privArray));
        }
        return rootQueryBuilder;
    }

    private static QueryBuilder getQuery(SearchField[] fields) {
        BoolQueryBuilder fieldQueryBuilder = QueryBuilders.boolQuery();
        for (int j = 0; j < fields.length; j++) {
            QueryBuilder queryBuilder = null;
            if (null != fields[j].getChildrenField()) {
                queryBuilder = getQuery(fields[j].getChildrenField());
            } else {
                queryBuilder = getSingleFieldQueryBuilder(fields[j]);
            }
            
            if(!fields[j].getNestedPath().isEmpty())
            {
            	queryBuilder = QueryBuilders.nestedQuery(fields[j].getNestedPath(), queryBuilder);
            }
           
            if (SearchConst.SEARCH_OPERATOR_SHOULD.equalsIgnoreCase(fields[j].getOperator())) {
                fieldQueryBuilder.should(queryBuilder);
            } else if (SearchConst.SEARCH_OPERATOR_MUST_NOT.equalsIgnoreCase(fields[j].getOperator())) {
                fieldQueryBuilder.mustNot(queryBuilder);
            } else {
                fieldQueryBuilder.must(queryBuilder);
            }
            
          
        }
        return fieldQueryBuilder;
    }

    private static QueryBuilder getSingleFieldQueryBuilder(SearchField searchField) {

        QueryBuilder queryBuilder = null;
        Object value = searchField.getSearchValue();
        String field = searchField.getField();
        if (SearchConst.SEARCH_METHOD_WILDCARD.equalsIgnoreCase(searchField.getMethod())) {
            queryBuilder = QueryBuilders.wildcardQuery(field, (String) value);

        } else if (SearchConst.SEARCH_METHOD_RANGE.equalsIgnoreCase(searchField.getMethod())) {

            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field);
            String comparison1 = searchField.getValueRanges().getComparison1();
            String comparison2 = searchField.getValueRanges().getComparison2();

            Object value1 = searchField.getValueRanges().getValue1();
            Object value2 = searchField.getValueRanges().getValue2();

            if (SearchConst.COMPARISON_OP_GT.equalsIgnoreCase(comparison1)) {
                rangeQueryBuilder.gt(value1);
            } else if (SearchConst.COMPARISON_OP_LT.equalsIgnoreCase(comparison1)) {
                rangeQueryBuilder.lt(value1);
            } else if (SearchConst.COMPARISON_OP_GTE.equalsIgnoreCase(comparison1)) {
                rangeQueryBuilder.gte(value1);
            } else if (SearchConst.COMPARISON_OP_LTE.equalsIgnoreCase(comparison1)) {
                rangeQueryBuilder.lte(value1);
            }

            if (SearchConst.COMPARISON_OP_GT.equalsIgnoreCase(comparison2)) {
                rangeQueryBuilder.gt(value2);
            } else if (SearchConst.COMPARISON_OP_LT.equalsIgnoreCase(comparison2)) {
                rangeQueryBuilder.lt(value2);
            } else if (SearchConst.COMPARISON_OP_GTE.equalsIgnoreCase(comparison2)) {
                rangeQueryBuilder.gte(value2);
            } else if (SearchConst.COMPARISON_OP_LTE.equalsIgnoreCase(comparison2)) {
                rangeQueryBuilder.lte(value2);
            }

            queryBuilder = rangeQueryBuilder;
        } else if (SearchConst.SEARCH_METHOD_MATCH.equalsIgnoreCase(searchField.getMethod())) {
            queryBuilder = QueryBuilders.matchQuery(field, value);
        } else if (SearchConst.SEARCH_METHOD_TERM.equalsIgnoreCase(searchField.getMethod())) {
            queryBuilder = QueryBuilders.termQuery(field, value);

        } else if (SearchConst.SEARCH_METHOD_TERMS.equalsIgnoreCase(searchField.getMethod())) {
            queryBuilder = QueryBuilders.termsQuery(field, value);

        } else if (SearchConst.SEARCH_METHOD_QUREY_STRING.equalsIgnoreCase(searchField.getMethod())) {
            queryBuilder = QueryBuilders.queryStringQuery(String.valueOf(value)).field(field);
        } else if (SearchConst.SEARCH_METHOD_EXISTS.equalsIgnoreCase(searchField.getMethod())) {
            queryBuilder = QueryBuilders.existsQuery(field);
        } else {
            queryBuilder = QueryBuilders.matchPhraseQuery(field, value);
        }

        if (searchField.isConstantScoreQuery()) {
            queryBuilder = QueryBuilders.constantScoreQuery(queryBuilder);
        }
        if (null != searchField.getBoost()) {
            if (BoostableQueryBuilder.class.isAssignableFrom(queryBuilder.getClass())) {
                ((BoostableQueryBuilder) queryBuilder).boost(searchField.getBoost());
            }
        }

        return queryBuilder;
    }

    /**
     * 按检索表达式进行搜索
     * 
     * @param si
     *            检索表达式输入，具体相关属性见QueryStringSearchInput，
     * @return 返回检索结果集
     * @throws IOException
     */
    public static SearchResult searchForQueryString(QueryStringSearchInput si) throws IOException {
        List<Map> list = new ArrayList<Map>();
        SearchResult sr = new SearchResult();
        getClient();

        String privs = SearchPriv.getPriv(si.isUseSystemID()?si.getSystemID():"");
        String[] privArray = privs.split(",");

        BoolQueryBuilder rootQueryBuilder = QueryBuilders.boolQuery();

        QueryStringQueryBuilder qsq = QueryBuilders.queryStringQuery(si.getQueryString());

        rootQueryBuilder.must(qsq);
        rootQueryBuilder.must(QueryBuilders.termsQuery(SearchConst.SYSTEMID_FIELD, privArray));
        SearchRequestBuilder srb = client.prepareSearch(si.getIndexName()).setQuery(rootQueryBuilder);

        String[] typeNames = si.getTypeNames();
        if (typeNames != null && typeNames.length > 0)
            srb.setTypes(typeNames);

        String[] routings = si.getRoutings();
        if (routings != null && routings.length > 0)
            srb.setRouting(routings);

        sort(si.getOrderFields(), srb);
        SearchResponse searchResponse = null;

        String[] returnFields = si.getReturnFields();
        if (returnFields != null && returnFields.length != 0) {
            srb.addFields(returnFields);
        }
        srb.setFrom(si.getFrom()).setSize(si.getSize());
        searchResponse = srb.execute().actionGet();

        SearchHits hits = searchResponse.getHits();

        SearchHit[] searchHists = hits.getHits();
        sr.setTotalNum(hits.getTotalHits());
        sr.setNum(searchHists.length);
        if (searchHists.length > 0) {
            for (SearchHit hit : searchHists) {
                getData(list, hit, si.getReturnFieldsAsHash());
            }
        }
        sr.setData(list);
        sr.setPageNum(si.getFrom()/si.getSize());
        sr.setPageSize(si.getSize());

        if (Debug)
            sr.setQuery(srb.toString());

        return sr;
    }

    /**
     * 按指定id值删除一条索引
     * 
     * @param indexName
     *            索引名称
     * @param type
     *            索引类型
     * @param id
     *            一条索引的的id至
     * @return 删除成功返回true，删除失败返回false
     * @throws IOException
     */
    public static String deleteIndex(String indexName, String type, String id) {
       
    	String errMsg = "";
    	 try{
    	    getClient();
       
        	DeleteResponse response = client.prepareDelete(indexName, type, id).setRefresh(true).execute().actionGet();
        	
        	if(!response.isFound())
        		errMsg = response.getId();
        }
        catch (Exception e) {
			// TODO Auto-generated catch block
        	errMsg = "errMsg: ";
			errMsg += indexName;
			errMsg += ", ";
			errMsg += type;
			errMsg += ", ";
			errMsg += id;
			errMsg += ", ";
			errMsg += e.getMessage();
		}
        
        return errMsg;
    }

    /**
     * 按DeleteInput属性进行删除索引
     * 
     * @param di
     *            删除输入，详细见DeleteInput类
     * @return 删除成功返回null，删除失败返回错误消息
     * @throws IOException
     */
    public static String deleteIndex(DeleteInput di){
        return deleteIndex(di.getIndexName(), di.getType(), di.getIds());
    }

    /**
     * 按id集合删除索引接口
     * 
     * @param indexName
     *            索引名称
     * @param type
     *            索引类型
     * @param ids
     *            索引id数组
     * @return 删除成功返回null，删除失败返回错误消息
     * @throws IOException
     */
    public static String deleteIndex(String indexName, String type, String[] ids) {
		String errMsg = "";
		try {
			getClient();
			ArrayList<String> delIDs = new ArrayList<String>();
			BulkRequestBuilder bulkRequest = client.prepareBulk();
			for (String id : ids) {

				DeleteRequestBuilder drb = client.prepareDelete(indexName, type, id);
				bulkRequest.add(drb);
			}
			bulkRequest.setRefresh(true);
			BulkResponse bulkResponse = bulkRequest.execute().actionGet();

			if (bulkResponse.hasFailures()) {
				errMsg = bulkResponse.buildFailureMessage();
//				BulkItemResponse[] birs = bulkResponse.getItems();
//				for (BulkItemResponse bir : birs) {
//					if (bir.isFailed()) {
//						if(!errMsg.isEmpty())
//							errMsg += ',';
//						errMsg += bir.getId();
//					}
//				}
			}
			else{
				BulkItemResponse[] birs = bulkResponse.getItems();
				for (BulkItemResponse bir : birs) {
					DeleteResponse res = (DeleteResponse)bir.getResponse();
					if (!res.isFound()) {
						if(!errMsg.isEmpty())
							errMsg += ',';
						errMsg += bir.getId();
					}
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			errMsg = "errMsg: ";
			errMsg += indexName;
			errMsg += ", ";
			errMsg += type;
			errMsg += ", ";
			errMsg += ids;
			errMsg += ", ";
			errMsg += e.getMessage();
		}

		return errMsg;
	}

    /**
     * 按检索条件删除索引，需要创建一个SearchInput 返回是否删除成功，数据量大或者系统内存资源少时不建议使用此接口
     * 
     * @param si
     *            检索输入，详细见SearchInput类
     * @return true为删除成功，false为删除失败
     * @throws ArgException
     * @throws IOException
     */
    public static boolean deleteIndexBySearch(SearchInput si) throws ArgException, IOException {

        getClient();
        SearchUtil.validateSI(si);

        BoolQueryBuilder queryBuilder = getMutilValueMutilFieldQueryBuilder(si.getSearchFields(),
                si.getSearchOperator(), si.isUseSystemID()?si.getSystemID():"");

        return false;
    }

    private static QueryBuilder getQueryBuilder(SearchInput si) throws ArgException

    {
        SearchUtil.validateSI(si);
        // BoolQueryBuilder queryBuilder =
        // getMutilValueQueryBuilder(si.getSearchFields()[0].getField(),si.getSearchFields()[0].getSearchValue(),si.getSystemID());

        BoolQueryBuilder queryBuilder = getMutilValueMutilFieldQueryBuilder(si.getSearchFields(),
                si.getSearchOperator(), si.isUseSystemID()?si.getSystemID():"");
        return queryBuilder;
    }

    /**
     * 聚合检索接口，详细使用见demo中样例
     * 
     * @param ai
     *            聚合检索输入，详细见AggInput类
     * @return 聚合检索结果集，详细见SearchAndAggResult类
     * @throws ElasticsearchException
     * @throws ArgException
     * @throws IOException 
     */
    public static SearchAndAggResult makeAggregation(AggInput ai) throws ElasticsearchException, ArgException, IOException {

        getClient();
        
        SearchRequestBuilder srb = client.prepareSearch(ai.getIndexName());

        String[] typeNames = ai.getTypeNames();
        if (typeNames != null && typeNames.length > 0)
            srb.setTypes(typeNames);

        String[] routings = ai.getRoutings();
        if (routings != null && routings.length > 0)
            srb.setRouting(routings);

        if (null != ai.getSi()) {
            ai.getSi().setIndexName(ai.getIndexName());
            ai.getSi().setTypeNames(ai.getTypeNames());

            srb.setQuery(getQueryBuilder(ai.getSi()));

            highLight(ai.getSi().getHighLightFields(), srb);
            sort(ai.getSi().getOrderFields(), srb);
            
            String[] returnFields = ai.getSi().getReturnFields();
            if (returnFields != null && returnFields.length != 0) {
                srb.addFields(returnFields);
            }
            
            srb.setFrom(ai.getSi().getFrom()).setSize(ai.getSi().getSize());
            srb.setPreference(ai.getSi().getPreference());
        }

        if (null != ai.getAggBuckets()) {
            List<AbstractAggregationBuilder> abs = getAggregationBuilders(ai.getAggBuckets());
            for (AbstractAggregationBuilder ab : abs)
                srb.addAggregation(ab);
        } else if (null != ai.getAggBucket()) {
            srb.addAggregation(getAggregationBuilder(ai.getAggBucket()));
        }

        SearchResponse srs = srb.execute().actionGet();

        List<SearchResult> srList = new ArrayList<SearchResult>();
        if (srs != null) {
            SearchHits hits = srs.getHits();

            SearchResult sr = new SearchResult();
            List<Map> list = new ArrayList<Map>();

            sr.setTotalNum(hits.getTotalHits());
            sr.setNum(hits.getHits().length);

            for (SearchHit hit : hits.getHits()) {
            	if(null != ai.getSi())
            		getData(list, hit, ai.getSi().getReturnFieldsAsHash());
            }
            sr.setData(list);
            srList.add(sr);
        }

        Aggregations aggregations = srs.getAggregations();

        AggResult aggResult = new AggResult();

        getBucketResult(aggregations, aggResult);

        SearchAndAggResult saar = new SearchAndAggResult();
        saar.setSearchResults(srList);
        saar.setAggResult(aggResult);

        if(null != ai.getSi())
        {
	        saar.setPageNum(ai.getSi().getFrom());
	        saar.setPageSize(ai.getSi().getSize());
        }
        if (Debug)
            saar.setQuery(srb.toString());

        return saar;
    }

    private static void getBucketResult(Aggregations aggregations, AggResult aggResult) {

        if (aggregations == null)
            return;

        for (Aggregation aggregation : aggregations) {

            if (MultiBucketsAggregation.class.isAssignableFrom(aggregation.getClass())) {

                Map<String, List<BucketResult>> bucketResultMap = aggResult.getBucketResultMap();
                if (bucketResultMap == null) {
                    bucketResultMap = new HashMap<String, List<BucketResult>>();
                    aggResult.setBucketResultMap(bucketResultMap);
                }

                List<BucketResult> bucketResults = bucketResultMap.get(aggregation.getName());
                if (null == bucketResults) {
                    bucketResults = new ArrayList<BucketResult>();
                    bucketResultMap.put(aggregation.getName(), bucketResults);
                }

                for (MultiBucketsAggregation.Bucket entry : ((MultiBucketsAggregation) aggregation).getBuckets()) {
                    BucketResult bucketResult = new BucketResult();
                    bucketResults.add(bucketResult);

                    bucketResult.setDocNumber(entry.getDocCount());
                    // Map<String, Number> metricsMap = new HashMap<String,
                    // Number>();

                    Map<String, AggResult> bucketsMap = new HashMap<String, AggResult>();
                    bucketResult.setBucketAgg(bucketsMap);

                    AggResult ar = new AggResult();
                    bucketsMap.put((String) entry.getKey(), ar);
                    if (null != entry.getAggregations()) {
                        getBucketResult(entry.getAggregations(), ar);
                    } else {
                        return;
                    }

                }
            } else {

                List<MetricsResult> metricsResults = aggResult.getMetricsResults();
                if (null == metricsResults) {
                    metricsResults = new ArrayList<MetricsResult>();
                    aggResult.setMetricsResults(metricsResults);
                }
                getMetricsResult(aggregation, metricsResults);
            }

        }

    }

    private static void getMetricsResult(Aggregation aggregation, List<MetricsResult> metricsResults) {

        MetricsResult mr = new MetricsResult();

        metricsResults.add(mr);

        mr.setAggName(aggregation.getName());

        List<Map<String, Number>> metrics = new ArrayList<Map<String, Number>>();

        mr.setMetrics(metrics);

        Map<String, Number> metricsMap = new HashMap<String, Number>();
        metrics.add(metricsMap);

        if (Avg.class.isAssignableFrom(aggregation.getClass())) {

            Avg avg = (Avg) aggregation;
            metricsMap.put(SearchConst.AGG_METRICS_AVG_KEY, avg.getValue());

        } else if (Sum.class.isAssignableFrom(aggregation.getClass())) {

            Sum sum = (Sum) aggregation;
            metricsMap.put(SearchConst.AGG_METRICS_SUM_KEY, sum.getValue());

        } else if (Max.class.isAssignableFrom(aggregation.getClass())) {

            Max max = (Max) aggregation;
            metricsMap.put(SearchConst.AGG_METRICS_MAX_KEY, max.getValue());

        } else if (Min.class.isAssignableFrom(aggregation.getClass())) {

            Min min = (Min) aggregation;
            metricsMap.put(SearchConst.AGG_METRICS_MIN_KEY, min.getValue());

        } else if (ValueCount.class.isAssignableFrom(aggregation.getClass())) {

            ValueCount count = (ValueCount) aggregation;
            metricsMap.put(SearchConst.AGG_METRICS_COUNT_KEY, count.getValue());

        } else if (Stats.class.isAssignableFrom(aggregation.getClass())) {
            Stats stats = (Stats) aggregation;

            metricsMap.put(SearchConst.AGG_METRICS_AVG_KEY, stats.getAvg());
            metricsMap.put(SearchConst.AGG_METRICS_SUM_KEY, stats.getSum());
            metricsMap.put(SearchConst.AGG_METRICS_MAX_KEY, stats.getMax());
            metricsMap.put(SearchConst.AGG_METRICS_MIN_KEY, stats.getMin());
            metricsMap.put(SearchConst.AGG_METRICS_COUNT_KEY, stats.getCount());

        } else if (ExtendedStats.class.isAssignableFrom(aggregation.getClass())) {

            ExtendedStats extStats = (ExtendedStats) aggregation;
            metricsMap.put(SearchConst.AGG_METRICS_AVG_KEY, extStats.getAvg());
            metricsMap.put(SearchConst.AGG_METRICS_SUM_KEY, extStats.getSum());
            metricsMap.put(SearchConst.AGG_METRICS_MAX_KEY, extStats.getMax());
            metricsMap.put(SearchConst.AGG_METRICS_MIN_KEY, extStats.getMin());
            metricsMap.put(SearchConst.AGG_METRICS_COUNT_KEY, extStats.getCount());

            metricsMap.put(SearchConst.AGG_METRICS_STD_DEVIATION_KEY, extStats.getStdDeviation());
            metricsMap.put(SearchConst.AGG_METRICS_SUM_OF_SQUARES_KEY, extStats.getSumOfSquares());
            metricsMap.put(SearchConst.AGG_METRICS_VARIANCE_KEY, extStats.getVariance());
        }
    }

    private static List<AbstractAggregationBuilder> getAggregationBuilders(List<AggBucket> AggBuckets) {

        List<AbstractAggregationBuilder> abs = new ArrayList();

        for (AggBucket topAggBucket : AggBuckets) {
            AbstractAggregationBuilder ab = null;
            ab = getSingleAggregationBuilder(topAggBucket);
            
//            if (MetricsAggregationBuilder.class.isAssignableFrom(ab.getClass())) {
//                abs.add(ab);
//            }

            if (null != topAggBucket.getAggBuckets()) {
                getAggregationBuilder((AggregationBuilder) ab, topAggBucket);
            }
            abs.add(ab);
        }

        return abs;
    }

    private static AbstractAggregationBuilder getAggregationBuilder(AggBucket topAggBucket) {

        AbstractAggregationBuilder ab = null;
        ab = getSingleAggregationBuilder(topAggBucket);
        if (MetricsAggregationBuilder.class.isAssignableFrom(ab.getClass())) {
            return ab;
        }

        if (null != topAggBucket.getAggBuckets()) {
            getAggregationBuilder((AggregationBuilder) ab, topAggBucket);
        }

        return ab;
    }

    private static void getAggregationBuilder(AggregationBuilder ab, AggBucket aggBucket) {
        List<AggBucket> aggBuckets = aggBucket.getAggBuckets();
        if (null == aggBuckets) {
            return;
        }

        for (AggBucket aggBucket1 : aggBucket.getAggBuckets()) {
            AbstractAggregationBuilder absAb = getSingleAggregationBuilder(aggBucket1);
            ab.subAggregation(absAb);

            getAggregationBuilder((AggregationBuilder) absAb, aggBucket1);
        }
    }

    private static AbstractAggregationBuilder getSingleAggregationBuilder(AggBucket aggBucket) {
        AbstractAggregationBuilder ab = null;

        if (null == aggBucket)
            return ab;
        String field = aggBucket.getField();
        if (null != field) { // 如果桶的field为空,则是指针对所有匹配文档做聚合运算
            ab = getAggregationTypeBuilder(aggBucket, 1);
        }

        List<AggMetrics> aggMetrics = aggBucket.getAggMetricses();
        if (null != aggMetrics && aggMetrics.size() != 0) {
            for (AggMetrics am : aggMetrics) {
                if (null != ab) {
                    ((AggregationBuilder) ab).subAggregation(getAggregationMetricsBuilder(am, 1));
                } else {
                    ab = getAggregationMetricsBuilder(am, 1);
                }
            }
        }

        return ab;

    }

    private static AggregationBuilder getAggregationTypeBuilder(AggBucket aggBucket, int level) {
        AggregationBuilder ab = null;
        int type = aggBucket.getType();
        String field = aggBucket.getField();
        String aggName = aggBucket.getAggName();
        int size = aggBucket.getSize();
        Terms.Order order = Terms.Order.count(false);
        if (aggBucket.getOrder() == SearchConst.SEARCH_ORDER_TYPE_DESC) {
            order = Terms.Order.term(false);
        } else if (aggBucket.getOrder() == SearchConst.SEARCH_ORDER_TYPE_ASC) {
            order = Terms.Order.term(true);
        }

        long inteval = aggBucket.getInterval();
        int dateInteval = aggBucket.getCustomerDateIntervalValue();

        switch (type) {
        case SearchConst.AGG_TYPE_TERMS:
            ab = AggregationBuilders.terms(aggName).field(field).order(order).size(size);
            break;
        case SearchConst.AGG_TYPE_HISTOGRAM:
            AggregationBuilder aggregation = AggregationBuilders.histogram(aggName).field(field).interval(inteval);
            break;
        case SearchConst.AGG_TYPE_DATE_HISTOGRAM:
            DateHistogramInterval dateInterval = null;
            int intervalType = aggBucket.getDateIntervalType();
            switch (intervalType) {
            case SearchConst.AGG_INTERVAL_YEAR:
                dateInterval = dateInterval.YEAR;
                break;
            case SearchConst.AGG_INTERVAL_QUARTER:
                dateInterval = dateInterval.QUARTER;
                break;
            case SearchConst.AGG_INTERVAL_MONTH:
                dateInterval = dateInterval.MONTH;
                break;
            case SearchConst.AGG_INTERVAL_DAY:
                dateInterval = dateInterval.DAY;
                break;
            case SearchConst.AGG_INTERVAL_WEEK:
                dateInterval = dateInterval.WEEK;
                break;
            case SearchConst.AGG_INTERVAL_HOUR:
                dateInterval = dateInterval.HOUR;
                break;
            case SearchConst.AGG_INTERVAL_MINUTE:
                dateInterval = dateInterval.MINUTE;
                break;
            case SearchConst.AGG_INTERVAL_SECOND:
                dateInterval = dateInterval.SECOND;
                break;
            case SearchConst.AGG_CUSTOMER_INTERVAL_DAY:
                dateInterval = dateInterval.days(dateInteval);
                break;
            case SearchConst.AGG_CUSTOMER_INTERVAL_HOUR:
                dateInterval = dateInterval.hours(dateInteval);
                break;
            case SearchConst.AGG_CUSTOMER_INTERVAL_MINUTE:
                dateInterval = dateInterval.minutes(dateInteval);
                break;
            case SearchConst.AGG_CUSTOMER_INTERVAL_SECOND:
                dateInterval = dateInterval.seconds(dateInteval);
                break;
            case SearchConst.AGG_CUSTOMER_INTERVAL_WEEK:
                dateInterval = dateInterval.weeks(dateInteval);
                break;
            default:
                break;
            }
            ab = AggregationBuilders.dateHistogram(aggName).field(field).interval(dateInterval);

            break;

        default:
            break;
        }

        return ab;
    }

    private static MetricsAggregationBuilder getAggregationMetricsBuilder(AggMetrics aggMetrics, int level) {
        MetricsAggregationBuilder ab = null;
        int metricsType = aggMetrics.getType();
        String aggName = aggMetrics.getAggName();
        String field = aggMetrics.getField();
        switch (metricsType) {
        case SearchConst.AGG_METRICS_MAX:
            ab = AggregationBuilders.max(aggName).field(field);
            break;
        case SearchConst.AGG_METRICS_MIN:
            ab = AggregationBuilders.min(aggName).field(field);
            break;
        case SearchConst.AGG_METRICS_SUM:
            ab = AggregationBuilders.sum(aggName).field(field);
            break;
        case SearchConst.AGG_METRICS_AVG:
            ab = AggregationBuilders.avg(aggName).field(field);
            break;
        case SearchConst.AGG_METRICS_COUNT:
            ab = AggregationBuilders.count(aggName).field(field);
            break;
        case SearchConst.AGG_METRICS_ALL:
            ab = AggregationBuilders.stats(aggName).field(field);
            break;
        case SearchConst.AGG_METRICS_ALL_EXTEND:
            ab = AggregationBuilders.stats(aggName).field(field);
            break;
        default:
            break;
        }
        return ab;

    }

    static List<String> spiltSentence(String document) {
        List<String> sentences = new ArrayList<String>();
        for (String line : document.split("[\r\n]")) {
            line = line.trim();
            if (line.length() == 0)
                continue;
            for (String sent : line.split("[，,。:：“”？?！!；;]")) {
                sent = sent.trim();
                if (sent.length() == 0)
                    continue;
                sentences.add(sent);
            }
        }

        return sentences;
    }
}
